﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace NSecurity.Providers
{
    public class AccessControlXmlProvider<TSecurity, TVerb, TSubject> : AccessControlMemoryProvider<TSecurity, TVerb, TSubject>
        where TSecurity : class
        where TVerb : class
        where TSubject : class
    {
        public override string Description
        {
            get { return "XML Access Control Provider"; }
        }

        public string FileName { get; set; }

        public XmlSerializer CreateSerializer(string root, Type type)
        {
            var xmlAttributes = new XmlAttributes
            {
                XmlRoot = new XmlRootAttribute
                {
                    ElementName = root
                }
            };

            var xmlAttributeOverrides = new XmlAttributeOverrides();
            xmlAttributeOverrides.Add(type, xmlAttributes);
            var serializer = new XmlSerializer(type, xmlAttributeOverrides);
            return serializer;
        }

        public override void Initialize(string name, NameValueCollection config)
        {
            base.Initialize(name, config);
            this.FileName = config["filename"];
            this.LoadAccessControl();
        }

        public override void PersistChanges()
        {
            var settings = new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true, CloseOutput = true };
            using (var writer = XmlWriter.Create(new StreamWriter(this.FileName), settings))
            {
                var serializer = this.CreateSerializer("acl", typeof(List<AccessControlEntry>));
                var ns = new XmlSerializerNamespaces();
                ns.Add(string.Empty, string.Empty);
                serializer.Serialize(writer, this.Entries, ns);
            }
        }

        public override void LoadAccessControl(string entries = null)
        {
            if (string.IsNullOrEmpty(entries))
            {
                // load rules are stored in a file
                try { entries = File.ReadAllText(this.FileName); }
                catch { return; }
            }

            using (var reader = new StreamReader(new MemoryStream(Encoding.Default.GetBytes(entries))))
            {
                var serializer = this.CreateSerializer("acl", typeof(List<AccessControlEntry>));
                this.Entries = (List<AccessControlEntry>)serializer.Deserialize(reader);
            }
        }
    }
}